package cn.hallele.bbts.core.component.aspect;

import cn.hallele.bbts.core.common.constants.MsgCodeConst;
import cn.hallele.bbts.core.common.constants.SymbolConstant;
import cn.hallele.bbts.core.common.exceptions.CCException;
import cn.hallele.bbts.core.common.exceptions.CoreException;
import cn.hallele.bbts.core.common.exceptions.UCException;
import cn.hallele.bbts.core.common.utils.HttpUtil;
import cn.hallele.bbts.core.component.io.BBTSHeader;
import cn.hallele.bbts.core.component.io.BBTSResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.validation.BindException;
import org.springframework.validation.method.ParameterValidationResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.HandlerMethodValidationException;

import java.util.List;
import java.util.StringJoiner;

/**
 * controller的异常拦截，拦截优先级 子类 > 父类
 *
 * @author anle5
 * @since 2023/11/29 星期三 23:54
 */
@Slf4j
@RestControllerAdvice
public class ExceptionAspect {
    @Autowired
    private MessageSource i18n;

    /**
     * 拦截所有CoreException的报错
     *
     * @param e {@link CoreException}
     * @return 错误响应
     */
    @ExceptionHandler(CoreException.class)
    public BBTSResponse<?> coreException(CoreException e) {
        String msgCode = e.getMessage();
        Object[] args = e.getArgs();
        String msgDesc = i18n.getMessage(msgCode, args, MsgCodeConst.FAILED, HttpUtil.getLocale());
        log.error("Find a CoreException. MsgCode: {}, msgDesc: {}.", msgCode, msgDesc, e);
        return buildResponse(msgCode, msgDesc);
    }

    /**
     * 拦截所有UCException的报错
     *
     * @param e {@link CCException}
     * @return 错误响应
     */
    @ExceptionHandler(UCException.class)
    public BBTSResponse<?> ucException(UCException e) {
        String msgCode = e.getMessage();
        Object[] args = e.getArgs();
        String msgDesc = i18n.getMessage(msgCode, args, MsgCodeConst.FAILED, HttpUtil.getLocale());
        log.error("Find a UCException. MsgCode: {}, msgDesc: {}.", msgCode, msgDesc, e);
        return buildResponse(msgCode, msgDesc);
    }

    /**
     * 拦截所有CCException的报错
     *
     * @param e {@link CCException}
     * @return 错误响应
     */
    @ExceptionHandler(CCException.class)
    public BBTSResponse<?> ccException(CCException e) {
        String msgCode = e.getMessage();
        Object[] args = e.getArgs();
        String msgDesc = i18n.getMessage(msgCode, args, MsgCodeConst.FAILED, HttpUtil.getLocale());
        log.error("Find a CCException. MsgCode: {}, msgDesc: {}.", msgCode, msgDesc, e);
        return buildResponse(msgCode, msgDesc);
    }

    /**
     * 拦截所有BindException的报错
     *
     * @param e {@link BindException}
     * @return 错误响应
     */
    @ExceptionHandler(BindException.class)
    public BBTSResponse<?> bindException(BindException e) {
        StringJoiner joiner = new StringJoiner(SymbolConstant.COMMA);
        e.getFieldErrors().forEach(fieldError -> {
            joiner.add(fieldError.getField());
        });
        String msgDesc = i18n.getMessage(
                MsgCodeConst.PARAMETER_VALIDATE_FAILED,
                new Object[]{joiner.toString(), ((MethodArgumentNotValidException) e).getBody().getDetail()},
                MsgCodeConst.FAILED,
                HttpUtil.getLocale());
        Object[] detailMessageArguments = ((MethodArgumentNotValidException) e).getDetailMessageArguments();
        Object message = SymbolConstant.UN_KNOWN;
        if (detailMessageArguments != null && detailMessageArguments.length == 2) {
            message = detailMessageArguments[1];
        }
        log.error("Find a BindException. MsgCode: {}, msgDesc: {}, details: {}",
                MsgCodeConst.PARAMETER_VALIDATE_FAILED, msgDesc, message, e);
        return buildResponse(MsgCodeConst.PARAMETER_VALIDATE_FAILED, msgDesc);
    }

    /**
     * 拦截所有HandlerMethodValidationException的报错
     *
     * @param e {@link HandlerMethodValidationException}
     * @return 错误响应
     */
    @ExceptionHandler(HandlerMethodValidationException.class)
    public BBTSResponse<?> handlerMethodValidationException(HandlerMethodValidationException e) {
        List<ParameterValidationResult> results = e.getAllValidationResults();
        StringBuilder sb = new StringBuilder();
        StringJoiner errorParams = new StringJoiner(SymbolConstant.COMMA);
        results.forEach(result -> {
            List<MessageSourceResolvable> resolvableErrors = result.getResolvableErrors();
            resolvableErrors.forEach(resolvableError -> {
                Object[] arguments = resolvableError.getArguments();
                if (arguments != null && arguments.length == 2) {
                    DefaultMessageSourceResolvable argument = (DefaultMessageSourceResolvable) arguments[0];
                    String paramName = argument.getDefaultMessage();
                    errorParams.add(paramName);
                    Object inputValue = arguments[1];
                    sb.append("paramName [").append(paramName)
                            .append("]--[").append(inputValue).append("] ")
                            .append("is not correct. It should be [");
                }
                String defaultMessage = resolvableError.getDefaultMessage();
                sb.append(defaultMessage).append("].");
            });
        });

        String msgDesc = i18n.getMessage(
                MsgCodeConst.PARAMETER_VALIDATE_FAILED,
                new Object[]{errorParams.toString(), e.getReason()},
                MsgCodeConst.FAILED,
                HttpUtil.getLocale());
        log.error("Find a HandlerMethodValidationException. MsgCode: {}, msgDesc: {}, details: {}",
                MsgCodeConst.PARAMETER_VALIDATE_FAILED, msgDesc, sb, e);
        return buildResponse(MsgCodeConst.PARAMETER_VALIDATE_FAILED, msgDesc);
    }

    /**
     * 拦截所有exception的报错
     *
     * @param e {@link Exception}
     * @return 错误响应
     */
    @ExceptionHandler(Exception.class)
    public BBTSResponse<?> exception(Exception e) {
        String msgDesc = i18n.getMessage(MsgCodeConst.FAILED, new Object[]{getErrorMsg(e)}, HttpUtil.getLocale());
        log.error("Find an Exception. MsgCode: {}, msgDesc: {}.", MsgCodeConst.FAILED, msgDesc, e);
        return buildResponse(MsgCodeConst.FAILED, msgDesc);
    }

    /**
     * 拦截所有throwable的报错
     *
     * @param e {@link Throwable}
     * @return 错误响应
     */
    @ExceptionHandler(Throwable.class)
    public BBTSResponse<?> throwable(Throwable e) {
        String msgDesc = i18n.getMessage(MsgCodeConst.FAILED, new Object[]{getErrorMsg(e)}, HttpUtil.getLocale());
        log.error("Find an Throwable. MsgCode: {}, msgDesc: {}.", MsgCodeConst.FAILED, msgDesc, e);
        return buildResponse(MsgCodeConst.FAILED, msgDesc);
    }

    private BBTSResponse<Object> buildResponse(String msgCode, String msgDesc) {
        return BBTSResponse.builder()
                .header(BBTSHeader.builder()
                        .success(MsgCodeConst.SUCCESS.equals(msgCode))
                        .msgCode(msgCode)
                        .msgDesc(msgDesc)
                        .ip(HttpUtil.getClientIp())
                        .accessDevice(HttpUtil.getClientPlatformName())
                        .build())
                .build();
    }

    private String getErrorMsg(Throwable e) {
        return StringUtils.isBlank(e.getMessage()) ? e.getCause().getMessage() : e.getMessage();
    }
}
